using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WPCoco;
using Microsoft.Xna.Framework;

namespace FluidDemo.Model
{
    public class PhisicalEnvironment
    {
        ParticleLocationQuery queryStructure = null;

        public ParticleLocationQuery Query
        {
            get { return queryStructure; }
            set { queryStructure = value; }
        }

        RectangleF bounds = new RectangleF(0, 0, 10, 10);

        public RectangleF Bounds
        {
            get { return bounds; }
            set 
            { 
                bounds = value;
                queryStructure = new GridListQuery(new Vector2(bounds.Width, bounds.Height), 
                                                   new Size((int)(bounds.Width / 5), (int)(bounds.Height / 5)));
            }
        }

        public Vector2 Gravity = new Vector2(0, 9.8f);

        public readonly List<Particle> Particles = new List<Particle>();

        public void AddParticle(Particle p)
        {
            Particles.Add(p);
        }

        public readonly List<IEntity> Entities = new List<IEntity>();

        public void AddEntity(IEntity obj) 
        {
            obj.Environment = this;
            Entities.Add(obj);
        }

        int frames = 0;

        int throthle = 4;

        public int Throthle
        {
            get { return throthle; }
            set { throthle = value; }
        }

        float elapsed = 0;

        public void Update(float dt)
        {
            // Time throtle
            frames++;
            elapsed += dt;
            if (throthle != 1 && frames % throthle == 0)
            {
                return;
            }

            queryStructure.Update(Particles);
            ApplyForces();
            Verlet(elapsed);
            SatisfyConstraints(elapsed);
            UpdateParticles();

            elapsed = 0;
        }

        void UpdateParticles()
        {
            for (int i = 0; i < Particles.Count; i++)
                Particles[i].Update();
        }

        void ApplyForces()
        {
            for (int i = 0; i < Particles.Count; i++)
                Particles[i].Force += Gravity;
        }

        void Verlet(float dt)
        {
            Particle p; Vector2 pos;
            for (int i = 0; i < Particles.Count; i++)
            {
                p = Particles[i];
                if (p.Energy < 0) p.Energy = 0;
                //pos = p.Position * 2 - p.Previous + p.Acceleration * (dt * dt) * 0.5f;
                pos = p.Position + p.Velocity * p.Energy + p.Acceleration * (dt * dt) * 0.5f;
                p.Previous = p.Position;
                p.Position = pos;
                p.Energy = 1;
                p.Force = Vector2.Zero;
            }
        }

        int collitions = 0;

        public int Collitions
        {
            get { return collitions; }
            private set { collitions = value; }
        }

        void SatisfyConstraints(float dt)
        {
            Vector2 min = new Vector2(bounds.X, bounds.Y);
            Vector2 max = new Vector2(bounds.X + bounds.Width, bounds.Y + bounds.Height);
            Particle p;
            for (int k = 0; k < 1; k++)
            {
                for (int i = 0; i < Particles.Count; i++)
                {
                    p = Particles[i];
                    for (int j = 0; j < p.Links.Count; j++)
                    {
                        SpringResolver.Solve(p, p.Links[j]);
                    }
                }

                int temp = 0, count;
                for (int i = 0; i < Particles.Count - 1; i++)
                {
                    Particle pa = Particles[i];
                    count = queryStructure.ResolveCollitions(pa.Position, new Vector2(pa.Radius * 2 + 5), pa);
                    temp += count;
                }

                for (int i = 0; i < Entities.Count; i++)
                    Entities[i].Solve();

                for (int i = 0; i < Particles.Count; i++)
                {
                    p = Particles[i];
                    Vector2 newpos = Vector2.Min(Vector2.Max(p.Position, min), max);
                    if (newpos != p.Position)
                    {
                        p.Position = newpos;
                        p.Energy -= 0.1f;

                        if (p.Position.X == min.X) p.Position.X += 0.1f;
                        if (p.Position.Y == min.Y) p.Position.Y += 0.1f;
                        if (p.Position.X == max.X) p.Position.X -= 0.1f;
                        if (p.Position.Y == max.Y) p.Position.Y -= 0.1f;
                    }
                }

                collitions = temp;
            }
        }
    }
}
